<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    var urls = [
      'http://jsonplaceholder.typicode.com/posts/1',
      'http://jsonplaceholder.typicode.com/posts/2',
      'http://jsonplaceholder.typicode.com/posts/3',
      'http://jsonplaceholder.typicode.com/posts/4',
      'http://jsonplaceholder.typicode.com/posts/5', 
      'http://jsonplaceholder.typicode.com/posts/6',
      'http://jsonplaceholder.typicode.com/posts/7',
      'http://jsonplaceholder.typicode.com/posts/8',
      'http://jsonplaceholder.typicode.com/posts/9',
      'http://jsonplaceholder.typicode.com/posts/10'
    ]

    function loadData (url) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.onload = function () {
          resolve(xhr.responseText)
        }
        xhr.open('GET', url)
        xhr.send()
      })
    }

    function limitLoad (urls, handler, limit, arr) {
        // 对数组进行一个拷贝
        const sequence = [...urls]
        let promises = [];

        // 实现并发请求达到最大值
        promises = sequence.splice(0, limit).map((url, index) => {
            // 这里返回的 index 是任务在数组 promises 的脚标
            //用于在 Promise.race 后找到完成的任务脚标
            return handler(url).then(response => {
              arr.push(response)
              return index
            }); 
        });

        // const p = Promise.race(promises)
        // p.then((index) => {
          
        // })

        // 利用数组的 reduce 方法来以队列的形式执行
        return sequence.reduce((last, url, currentIndex) => {
          return last.then(() => {
            // 返回最快改变状态的 Promise
            return Promise.race(promises)
          }).then((index) => {
            // 用新的 Promise 替换掉最快改变状态的 Promise
            promises[index] = handler(sequence[currentIndex]).then(response => {
              arr.push(response)
              return index
            })
          }).catch(err => {
              // 这里的 catch 不仅用来捕获前面 then 方法抛出的错误
              // 更重要的是防止中断整个链式调用
              console.error(err)
          })
        }, Promise.resolve()).then(() => {
          return Promise.all(promises)
        })
        
    }
    
    (async function () {
      const arr = []
      await limitLoad(urls, loadData, 3, arr)
      console.log(arr)
    })()

  </script>
</body>
</html>